"""
Frappe GS - GreatSQL数据库兼容扩展

此模块通过猴子补丁方式修改Frappe框架的数据库相关功能，以提供更好的GreatSQL数据库兼容性。
主要功能包括：
1. 强制使用InnoDB引擎
2. 增强索引管理功能
3. 优化数据类型定义
4. 改进序列支持
"""

__version__ = "0.0.1"

from frappe.database.mariadb.schema import MariaDBTable
from frappe.database.mariadb.database import MariaDBDatabase
from frappe.database.schema import DbColumn
import frappe
from functools import wraps
from frappe.database.schema import get_definition as get_field_definition
from frappe.utils import cint, flt, cstr
from pymysql.constants.ER import DUP_ENTRY
from frappe import _

def patch_mariadb_table():
    """修改MariaDBTable.create方法，强制使用InnoDB引擎
    
    通过猴子补丁方式修改create方法，确保所有新建的表都使用InnoDB引擎，
    以提供更好的事务支持和数据一致性。
    """
    if not hasattr(MariaDBTable, '_original_create'):
        MariaDBTable._original_create = MariaDBTable.create
        
        def create(self):
            # 强制使用InnoDB引擎
            self.meta.engine = "InnoDB"
            return MariaDBTable._original_create(self)
            
        MariaDBTable.create = create

def patch_mariadb_table_alter():
    """修改MariaDBTable.alter方法，增强索引管理功能
    
    主要改进：
    1. 增加唯一索引存在性检查
    2. 使用IF NOT EXISTS语法添加唯一索引
    3. 智能处理索引的添加和删除
    4. 优化错误处理和提示信息
    """
    if not hasattr(MariaDBTable, '_original_alter'):
        MariaDBTable._original_alter = MariaDBTable.alter
        
        def alter(self):
            # 处理列定义
            for col in self.columns.values():
                col.build_for_alter_table(self.current_columns.get(col.fieldname.lower()))

            # 构造添加列的SQL语句
            add_column_query = [f"ADD COLUMN `{col.fieldname}` {col.get_definition()}" for col in self.add_column]
            
            # 构造修改列的SQL语句
            columns_to_modify = set(self.change_type + self.set_default)
            modify_column_query = [
                f"MODIFY `{col.fieldname}` {col.get_definition(for_modification=True)}"
                for col in columns_to_modify
            ]
            
            # 构造添加唯一索引的SQL语句
            modify_column_query.extend(
                [f"ADD UNIQUE INDEX IF NOT EXISTS {col.fieldname} (`{col.fieldname}`)"
                for col in self.add_unique
                if not frappe.db.get_column_index(self.table_name, col.fieldname, unique=True)]
            )
            
            # 构造添加普通索引的SQL语句
            add_index_query = [
                f"ADD INDEX `{col.fieldname}_index`(`{col.fieldname}`)"
                for col in self.add_index
                if not frappe.db.get_column_index(self.table_name, col.fieldname, unique=False)
            ]

            # 为creation字段添加索引
            if self.meta.sort_field == "creation" and not frappe.db.get_column_index(
                self.table_name, "creation", unique=False
            ):
                add_index_query.append("ADD INDEX `creation`(`creation`)")

            # 构造删除索引的SQL语句
            drop_index_query = []

            for col in {*self.drop_index, *self.drop_unique}:
                if col.fieldname == "name":
                    continue

                current_column = self.current_columns.get(col.fieldname.lower())
                unique_constraint_changed = current_column.unique != col.unique
                if unique_constraint_changed and not col.unique:
                    if unique_index := frappe.db.get_column_index(self.table_name, col.fieldname, unique=True):
                        drop_index_query.append(f"DROP INDEX `{unique_index.Key_name}`")

                index_constraint_changed = current_column.index != col.set_index
                if index_constraint_changed and not col.set_index:
                    if index_record := frappe.db.get_column_index(self.table_name, col.fieldname, unique=False):
                        drop_index_query.append(f"DROP INDEX `{index_record.Key_name}`")

            try:
                # 执行SQL语句
                for query_parts in [add_column_query, modify_column_query, add_index_query, drop_index_query]:
                    if query_parts:
                        query_body = ", ".join(query_parts)
                        query = f"ALTER TABLE `{self.table_name}` {query_body}"
                        frappe.db.sql_ddl(query)

            except Exception as e:
                # 处理错误
                if query := locals().get("query"):  
                    print(f"Failed to alter schema using query: {query}")

                if e.args[0] == DUP_ENTRY:
                    fieldname = str(e).split("'")[-2]
                    frappe.throw(
                        _(
                            "{0} field cannot be set as unique in {1}, as there are non-unique existing values"
                        ).format(fieldname, self.table_name)
                    )

                raise
            
        MariaDBTable.alter = alter

def patch_mariadb_database():
    """修改MariaDBDatabase.create_global_search_table方法
    
    确保全局搜索表使用InnoDB引擎，提供更好的事务支持。
    """
    if not hasattr(MariaDBDatabase, '_original_create_global_search_table'):
        MariaDBDatabase._original_create_global_search_table = MariaDBDatabase.create_global_search_table
        
        def create_global_search_table(self):
            # 检查全局搜索表是否存在
            if "__global_search" not in self.get_tables():
                # 创建全局搜索表
                self.sql(
                    f"""create table __global_search(
                    doctype varchar(100),
                    name varchar({self.VARCHAR_LEN}),
                    title varchar({self.VARCHAR_LEN}),
                    content text,
                    fulltext(content),
                    route varchar({self.VARCHAR_LEN}),
                    published int(1) not null default 0,
                    unique `doctype_name` (doctype, name))
                    COLLATE=utf8mb4_unicode_ci
                    ENGINE=InnoDB
                    CHARACTER SET=utf8mb4"""
                )
            
        MariaDBDatabase.create_global_search_table = create_global_search_table

def patch_db_column():
    """修改DbColumn.get_definition方法
    
    为text和longtext类型的默认值添加括号，优化字段定义方式。
    """
    if not hasattr(DbColumn, '_original_get_definition'):
        DbColumn._original_get_definition = DbColumn.get_definition
        
        def get_column_definition(self, for_modification=False):
            # 获取字段定义
            column_def = get_field_definition(self.fieldtype, precision=self.precision, length=self.length)

            if not column_def:
                return column_def

            # 处理默认值
            if self.fieldtype in ("Check", "Int"):
                default_value = cint(self.default) or 0
                column_def += f" not null default {default_value}"

            elif self.fieldtype in ("Currency", "Float", "Percent"):
                default_value = flt(self.default) or 0
                column_def += f" not null default {default_value}"

            elif (
                self.default
                and (self.default not in frappe.db.DEFAULT_SHORTCUTS)
                and not cstr(self.default).startswith(":")
            ):
                if self.fieldtype not in ("text", "longtext"):
                    column_def += f" default {frappe.db.escape(self.default)}"
                else:
                    column_def += f" default ({frappe.db.escape(self.default)})"

            # 处理唯一索引
            if self.unique and not for_modification and (self.fieldtype not in ("text", "longtext")):
                column_def += " unique"

            return column_def
            
        DbColumn.get_definition = get_column_definition

def patch_schema():
    """修改schema模块的add_column方法
    
    增加列存在性检查，避免重复添加列导致的错误。
    """
    from frappe.database.schema import add_column as original_add_column
    
    def new_add_column(doctype, column_name, fieldtype, precision=None, length=None, default=None, not_null=False):
        # 检查列是否存在
        if frappe.db.has_column(doctype, column_name):
            return
            
        # 提交事务
        frappe.db.commit()
        
        # 构造添加列的SQL语句
        query = "alter table `tab{}` add column {} {}".format(
            doctype,
            column_name,
            get_definition(fieldtype, precision, length),
        )

        # 处理非空约束
        if not_null:
            query += " not null"
        
        # 处理默认值
        if default:
            query += f" default '{default}'"

        # 执行SQL语句
        frappe.db.sql(query)
        
    from frappe.database import schema
    schema.add_column = new_add_column

def patch_sequence():
    """修改get_next_val方法
    
    使用新的序列语法，提供更好的自增序列支持。
    """
    from frappe.database.sequence import get_next_val as original_get_next_val
    from frappe.database import sequence
    
    def new_get_next_val(doctype_name: str, slug: str = "_id_seq") -> int:
        # 获取序列名称
        sequence_name = sequence.scrub(f"{doctype_name}{slug}")

        # 处理序列名称
        if sequence.db.db_type == "postgres":
            sequence_name = f'"\"{sequence_name}\""'
        elif sequence.db.db_type == "mariadb":
            sequence_name = f"`{sequence_name}`"

        try:
            # 获取下一个序列值
            return sequence.db.sql(f"SELECT {sequence_name}.nextval")[0][0]
        except IndexError:
            # 处理序列值溢出
            raise sequence.db.SequenceGeneratorLimitExceeded
            
    sequence.get_next_val = new_get_next_val

def init():
    """初始化所有补丁
    
    按照正确的顺序应用所有猴子补丁，确保功能正常工作。
    """
    patch_mariadb_table()
    patch_mariadb_table_alter()
    patch_mariadb_database()
    patch_db_column()
    patch_schema()
    patch_sequence()

# 立即应用补丁
init()
